﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Collections;
using System.IO;

namespace SlopeViewer
{
    public partial class Form1 : Form
    {
        // Total view data count = 2 * SLOPE_VIEW_WINDOW_SIZE
        private const int SLOPE_VIEW_WINDOW_SIZE = 2500;

        private List<SamplePoint> _samples = null;
        private EditForm _editWindow = null;

        public Form1()
        {
            InitializeComponent();

            //qt_MovieImpl.QTControl.StatusUpdate += OnMovieStatusUpdate;
            plot_Slope.Smoothing = System.Drawing.Drawing2D.SmoothingMode.None;
            splt.Size = new Size(splt.Size.Width, this.ClientSize.Height - panel_Info.Size.Height - menuStrip1.Size.Height);
        }

        private void OnOpenFile(object sender, EventArgs e)
        {
            // Open movie file
            OpenFileDialog dialogMovie = new OpenFileDialog();
            dialogMovie.Filter = "Movie files (*.mp4; *.mov)|*.mp4; *.mov";
            dialogMovie.InitialDirectory = Application.StartupPath;
            dialogMovie.Title = "Select a movie file";
            if (dialogMovie.ShowDialog() != DialogResult.OK) return;

            string moviePath = dialogMovie.FileName;
            string metadataPath = Path.GetDirectoryName(moviePath) + "\\" +  Path.GetFileNameWithoutExtension(moviePath) + ".txt";

            if (!File.Exists(metadataPath))
            {
                MessageBox.Show(String.Format("Cannot find corresponding metadata file at : {0}", metadataPath));
                return;
            }

            // Open movie
            qt_Control.URL = moviePath;

            // Open Metadata
             _samples = new List<SamplePoint> ();

            // Read the file and display it line by line.
            float xMin = float.MaxValue;
            float xMax = float.MinValue;
            float pitchMax = float.MinValue;
            float pitchMin = float.MaxValue;
            float rollMax = float.MinValue;
            float rollMin = float.MaxValue;

            using (StreamReader file = new StreamReader(metadataPath))
            {
                string line;
                char[] delimiter = {'\t'};
                while ((line = file.ReadLine()) != null)
                {
                    string[] words = line.Split(delimiter);
                    SamplePoint sample = new SamplePoint();
                    sample.Mileage = float.Parse(words[1]);
                    sample.Pitch = float.Parse(words[2]);
                    sample.Roll = float.Parse(words[3]);
                    _samples.Add(sample);

                    if (sample.Mileage > xMax) xMax = sample.Mileage;
                    if (sample.Mileage < xMin) xMin = sample.Mileage;
                    if (sample.Pitch > pitchMax) pitchMax = sample.Pitch;
                    if (sample.Pitch < pitchMin) pitchMin = sample.Pitch;
                    if (sample.Roll > rollMax) rollMax = sample.Roll;
                    if (sample.Roll < rollMin) rollMin = sample.Roll;
                }
            }

            // Update label
            lbl_DataCount.Text = String.Format("Metadata Count : {0}", _samples.Count);
            lbl_DataIndex.Text = String.Format("Metadata Current Index : 0");
            lbl_Pitch.Text = String.Format("Pitch : {0}", _samples[0].Pitch);
            lbl_Roll.Text = String.Format("Roll : {0}", _samples[0].Roll);
            lbl_Mileage.Text = String.Format("Mileage : {0}", _samples[0].Mileage);

            // Update slider properties
            track_Time.Minimum = 0;
            track_Time.Maximum = _samples.Count - 1;
            track_Time.Enabled = true;

            // Show edit window
            _editWindow = new EditForm(_samples);
            _editWindow.Location = new Point(this.Location.X + this.Size.Width, this.Location.Y);
            _editWindow.OnApplyChange += OnApplyChange;
            _editWindow.Show();

            this.SuspendLayout();

            plot_Slope.PanelLayout = GraphLib.PlotterGraphPaneEx.LayoutMode.TILES_VER;
            plot_Slope.DataSources.Clear();
            plot_Slope.SetDisplayRangeX(xMin, 5000.0f);
            plot_Slope.SetGridDistanceX(500);

            plot_Slope.SetDisplayRangeX(-SLOPE_VIEW_WINDOW_SIZE, SLOPE_VIEW_WINDOW_SIZE);

            plot_Slope.DataSources.Add(new GraphLib.DataSource());
            plot_Slope.DataSources[0].Name = "Pitch Graph";
            plot_Slope.DataSources[0].OnRenderXAxisLabel += OnRenderXLabel;
            plot_Slope.DataSources[0].OnRenderYAxisLabel += OnRenderYLabel;
            plot_Slope.DataSources[0].Length = _samples.Count;
            plot_Slope.DataSources[0].SetDisplayRangeY(pitchMin, pitchMax);
            plot_Slope.DataSources[0].SetGridDistanceY(5.0f);

            plot_Slope.DataSources.Add(new GraphLib.DataSource());
            plot_Slope.DataSources[1].Name = "Roll Graph";
            plot_Slope.DataSources[1].OnRenderXAxisLabel += OnRenderXLabel;
            plot_Slope.DataSources[1].OnRenderYAxisLabel += OnRenderYLabel;
            plot_Slope.DataSources[1].Length = _samples.Count;
            plot_Slope.DataSources[1].SetDisplayRangeY(rollMin, rollMax);
            plot_Slope.DataSources[1].SetGridDistanceY(100);
            plot_Slope.DataSources[1].SetGridDistanceY(5.0f);

            for (int i = 0; i < _samples.Count; ++i)
            {
                plot_Slope.DataSources[0].Samples[i].x = i;
                plot_Slope.DataSources[0].Samples[i].y = _samples[i].Pitch;

                plot_Slope.DataSources[1].Samples[i].x = i;
                plot_Slope.DataSources[1].Samples[i].y = _samples[i].Roll;
            }

            this.ResumeLayout();
            plot_Slope.Refresh();
        }

        private String OnRenderXLabel(GraphLib.DataSource s, int idx)
        {
            String Label = "" + s.Samples[idx].x;
            return Label;
        }

        private String OnRenderYLabel(GraphLib.DataSource s, float value)
        {
            return String.Format("{0:0.0}", value);
        }

        private bool _movieLoaded;
        private Size _originalMovieSize;
     
        private void qt_Control_StatusUpdate(object sender, AxQTOControlLib._IQTControlEvents_StatusUpdateEvent e)
        {
            if (e.statusCode == 100000)
            {
                _originalMovieSize = new Size(qt_Control.Movie.Width, qt_Control.Movie.Height);
                qt_Control.ScaleX = (float)splt.Panel1.Width / _originalMovieSize.Width;
                qt_Control.ScaleY = (float)splt.Panel1.Height / _originalMovieSize.Height;

                _movieLoaded = true;

                // Update Movie length
                lbl_MovieLength.Text = String.Format("Movie Length : {0} seconds", (float)qt_Control.Movie.EndTime/qt_Control.Movie.TimeScale);
                lbl_MovieCurrentTime.Text = String.Format("Movie Current Time : {0:0.000} seconds", (float)qt_Control.Movie.Time / qt_Control.Movie.TimeScale);
            }
        }

        private void splt_Panel1_SizeChanged(object sender, EventArgs e)
        {
            if (_movieLoaded)
            {
                qt_Control.ScaleX = (float)splt.Panel1.Width / _originalMovieSize.Width;
                qt_Control.ScaleY = (float)splt.Panel1.Height / _originalMovieSize.Height;
            }
        }

        private void track_Time_ValueChanged(object sender, EventArgs e)
        {
            int currentIndex = track_Time.Value;

            // Update data index
            lbl_DataIndex.Text = String.Format("Metadata Current Index : {0}", currentIndex);
            lbl_Pitch.Text = String.Format("Pitch : {0}", _samples[currentIndex].Pitch);
            lbl_Roll.Text = String.Format("Roll : {0}", _samples[currentIndex].Roll);
            lbl_Mileage.Text = String.Format("Mileage : {0}", _samples[currentIndex].Mileage);

            // Update movie time
            float ratio = (float)currentIndex / _samples.Count;
            qt_Control.Movie.Time = (int)(ratio * qt_Control.Movie.EndTime);
            lbl_MovieCurrentTime.Text = String.Format("Movie Current Time : {0:0.000} seconds", (float)qt_Control.Movie.Time / qt_Control.Movie.TimeScale);

            // Update graph
            plot_Slope.SetDisplayRangeX(currentIndex - SLOPE_VIEW_WINDOW_SIZE, currentIndex + SLOPE_VIEW_WINDOW_SIZE);
            plot_Slope.Refresh();
        }

        private void Form1_SizeChanged(object sender, EventArgs e)
        {
            if (_editWindow != null)
            {
                _editWindow.Location = new Point(this.Location.X + this.Size.Width, this.Location.Y);
            }

            splt.Size = new Size(splt.Size.Width, this.ClientSize.Height - panel_Info.Size.Height - menuStrip1.Size.Height);
        }

        private void Form1_LocationChanged(object sender, EventArgs e)
        {
            if (_editWindow != null)
            {
                _editWindow.Location = new Point(this.Location.X + this.Size.Width, this.Location.Y);
            }
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized)
            {
                if (_editWindow != null)
                {
                    _editWindow.WindowState = FormWindowState.Minimized;
                }
            }
            else if (WindowState == FormWindowState.Normal)
            {
                if (_editWindow != null)
                {
                    _editWindow.WindowState = FormWindowState.Normal;
                }
            }
        }

        private void OnApplyChange(List<SamplePoint> samples)
        {
            float pitchMax = float.MinValue;
            float pitchMin = float.MaxValue;
            float rollMax = float.MinValue;
            float rollMin = float.MaxValue;

            for (int i = 0; i < samples.Count; ++i)
            {
                plot_Slope.DataSources[0].Samples[i].x = i;
                plot_Slope.DataSources[0].Samples[i].y = samples[i].Pitch;

                plot_Slope.DataSources[1].Samples[i].x = i;
                plot_Slope.DataSources[1].Samples[i].y = samples[i].Roll;

                if (samples[i].Pitch > pitchMax) pitchMax = samples[i].Pitch;
                if (samples[i].Pitch < pitchMin) pitchMin = samples[i].Pitch;
                if (samples[i].Roll > rollMax) rollMax = samples[i].Roll;
                if (samples[i].Roll < rollMin) rollMin = samples[i].Roll;
            }

            plot_Slope.DataSources[0].SetDisplayRangeY(pitchMin, pitchMax);
            plot_Slope.DataSources[1].SetDisplayRangeY(rollMin, rollMax);
            plot_Slope.Refresh();
        }
    }
}
